#include "SQLParser.h"
#include <cctype>

using namespace std;
using namespace EFLite;

vector<CD> recoverCDs(vector<string>);
Predicate *recoverWhere(string);
Predicate *recoverWhere(string p, int &pos);
Predicate *parseBinaryPredicate(string s);

int SQLParser::Parse(string Query, Entity *result)
{
	for(int i = 0; i < (int)Query.length(); i++)
	{
		Query.at(i) = (char)tolower(Query[i]);
	}
	
	bool selecting = Query.find("select") != string::npos;
	bool inserting = Query.find("insert into") != string::npos;
	bool updating = Query.find("update") != string::npos;
	bool deleting = Query.find("delete from") != string::npos;
	bool creating = Query.find("create table") != string::npos;
	bool dropping = Query.find("drop table") != string::npos;
	int wherePos = Query.find("where");
	Predicate *p = NULL;

	if(wherePos != string::npos)
	{
		string whereString;
		sscanf(Query.substr(wherePos).c_str(), "where %s", whereString);

		p = recoverWhere(whereString);
	}

	if(selecting)
	{
		string columns = "";
		string tableName = "";
		sscanf(Query.c_str(),"select %s from %s",columns, tableName);
		
		vector<string> cols = split(columns, ',');
		vector<string> tables = split(tableName, ',');

		ResolveSelect(cols, tables, p, result);
		return 0;
	}
	if(inserting) 
	{
		string tableName = "";
		sscanf(Query.c_str(),"insert into %s values",tableName);

		int start = Query.find("values")+8;
		string values = Query.substr(start);

		return 0;
	}
	if(updating) 
	{
		char* tableName = NULL;
		sscanf(Query.c_str(),"update %s set",tableName);

		return 0;
	}
	if(deleting) 
	{
		string tableName = "";
		sscanf(Query.c_str(),"delete from %s",tableName);

		ResolveDelete(tableName, p);
		return 0;
	}
	if(creating) 
	{
		char* tableName = NULL;

		const char* we = Query.c_str();
		sscanf(we,"create table %s ",tableName);

		int start = Query.find("(")+1;
		string columns = Query.substr(start,Query.length()-start-1);

		vector<CD> cds = recoverCDs(split(columns,','));

		ResolveCreate(string(tableName), cds);
		return 0;
	}
	if(dropping) 
	{
		string tableName = "";
		sscanf(Query.c_str(),"drop table %s",tableName);

		ResolveDrop(tableName);
		return 0;
	}

	return 1;
}

Predicate *recoverWhere(string p)
{
	int derp = 0;
	return recoverWhere(p, derp);
}

Predicate *recoverWhere(string p, int &pos)
{

	if(p.substr(pos, 3) == "not")								// Handle the unary not
	{
		Predicate *pred;

		pos += 4;

		pred = recoverWhere(p, pos);

		return new NOT(pred);
	}
	else if(p[pos] == '(')										// Meta binary predicate. We need to go one level deeper
	{
		pos++;
		Predicate *l, *r;
		l = recoverWhere(p, pos);								// Recurse and find the predicate under this one
		pos += 2;
		int opLen = p.find(' ', pos) - pos;

		string op = p.substr(pos, opLen);						// Get the operator

		pos += opLen + 2;
		r = recoverWhere(p, pos);								// Recover the other predicate
		pos++;

		if(op == "and")
			return new AND(l,r);
		if(op == "or")
			return new OR(l,r);
		else
			return NULL;
	}
	else														// Here we have a non-meta binary predicate
	{
		int endPos = p.find(")", pos) - pos;
		pos += endPos;
		return parseBinaryPredicate(p.substr(pos, endPos));
	}

}


#pragma warning (disable:4996)
string removeSpacesInStringLiterals(string s)
{
	int strLength = s.length();
	char* buf = new char[strLength];
	s.copy(buf, 0, strLength);
	bool inString = false;
	for(int i=0; i<strLength; i++)
	{
		if(inString && buf[i]==' ')
			buf[i]='_';
		if(buf[i]=='\'') //assuming no escaped single quotes
			inString = !inString;
	}
	string ret(buf, buf+strLength);
	delete [] buf;
	return ret;
}

void parseValue(string val, string &name, Cell *&cell )
{
	int val1 = 0, val2 = 0, val3 = 0;
	float fval1 = 0;

	if(sscanf(val.c_str(), "%d/%d/%d", val1, val2, val3) == 3)				// Do we have a date?
		cell = new Date(val1, val2, val3);
	else if(sscanf(val.c_str(), "%d:%d:%d", val1, val2, val3))				// A time?
		cell = new Time(val1, val2, val3);
	else if(sscanf(val.c_str(), "%d", val1))								// An int?
		cell = new Int(val1);
	else if(sscanf(val.c_str(), "%f", fval1))								// A float?
		cell = new Float(fval1);
	else if(val.find('\'') == 0 && val.find('\'', 1) == val.length() - 1)	// A string literal?
		cell = new String(val.substr(1,val.length() - 1));
	else																	// It wasn't a literal value, so it must be a column name
		name = val;
}

Predicate *parseBinaryPredicate(string s)
{
	vector<string> tokens = split(removeSpacesInStringLiterals(s),' ');		// Fix any string literals, then tokenize the input

	string lvalue = "", rvalue = "";
	Cell *lcell = 0, *rcell = 0;

	parseValue(tokens[0], lvalue, lcell);									// Get our values
	parseValue(tokens[2], rvalue, rcell);

	if( lvalue == "" && lcell == 0 || rvalue == "" && rcell == 0)
		throw new exception("A value wasn't found correctly");

	// This is so ugly

	if( tokens[1] == "=")
	{
		if(lcell == NULL && rcell == NULL)
			return new EQ(lvalue, rvalue);
		if(lvalue == "" && rcell == NULL)
			return new EQ(lcell, rvalue);
		if(lcell == NULL && rvalue == "")
			return new EQ(lvalue, rcell);
		if(lvalue == "" && rvalue == "")
			return new EQ(lcell,rcell);
	}
	if( tokens[1] == "<>")
	{
		if(lcell == NULL && rcell == NULL)
			return new NEQ(lvalue, rvalue);
		if(lvalue == "" && rcell == NULL)
			return new NEQ(lcell, rvalue);
		if(lcell == NULL && rvalue == "")
			return new NEQ(lvalue, rcell);
		if(lvalue == "" && rvalue == "")
			return new NEQ(lcell,rcell);
	}
	if( tokens[1] == "<")
	{
		if(lcell == NULL && rcell == NULL)
			return new LT(lvalue, rvalue);
		if(lvalue == "" && rcell == NULL)
			return new LT(lcell, rvalue);
		if(lcell == NULL && rvalue == "")
			return new LT(lvalue, rcell);
		if(lvalue == "" && rvalue == "")
			return new LT(lcell,rcell);
	}
	if( tokens[1] == "<=")
	{
		if(lcell == NULL && rcell == NULL)
			return new LTE(lvalue, rvalue);
		if(lvalue == "" && rcell == NULL)
			return new LTE(lcell, rvalue);
		if(lcell == NULL && rvalue == "")
			return new LTE(lvalue, rcell);
		if(lvalue == "" && rvalue == "")
			return new LTE(lcell,rcell);
	}
	if( tokens[1] == ">")
	{
		if(lcell == NULL && rcell == NULL)
			return new GT(lvalue, rvalue);
		if(lvalue == "" && rcell == NULL)
			return new GT(lcell, rvalue);
		if(lcell == NULL && rvalue == "")
			return new GT(lvalue, rcell);
		if(lvalue == "" && rvalue == "")
			return new GT(lcell,rcell);
	}
	if( tokens[1] == ">=")
	{
		if(lcell == NULL && rcell == NULL)
			return new GTE(lvalue, rvalue);
		if(lvalue == "" && rcell == NULL)
			return new GTE(lcell, rvalue);
		if(lcell == NULL && rvalue == "")
			return new GTE(lvalue, rcell);
		if(lvalue == "" && rvalue == "")
			return new GTE(lcell,rcell);
	}
	return NULL;
}

vector<CD> recoverCDs(vector<string> columnNames)
{
	vector<CD> columnDefs;
	Types t;

	for each (string s in columnNames)
	{
		vector<string> args = split(s,' ');
		if(args[1].find("varchar") != string::npos)				// Set the type of the CD
		{
			//int size;
			//sscanf(args[1].c_str(),"varchar(%d)",size);
			t = VARCHAR;
		}
		if(args[1].find("float") != string::npos){t = FLOAT;}
		if(args[1].find("int") != string::npos){t = INT;}
		if(args[1].find("date") != string::npos){t = DATE;}
		if(args[1].find("time") != string::npos){t = TIME;}
		columnDefs.push_back(CD(args[0], t));
	}

	return columnDefs;
}

void *createRowAddr(list<CD> cds, vector<ColumnValuePair> cvs)
{
	void* newRecordAddr = (void*) operator new (sizeOfCD(cds));
	void* insertAddr = newRecordAddr;
	void* oldAddr;
	list<CD>::iterator i = cds.begin();

	while(i != cds.end())
	{
		oldAddr = insertAddr;
		for(int j = 0; j < (int)cvs.size(); j++)
		{
			if(i->GetName() == cvs.at(j).columnName)
			{
				switch(i->GetType()) {
					case DATE:
						*((Date*)insertAddr) = *((Date*)cvs.at(j).Value);
						insertAddr =  ((Date*)(insertAddr))+1;
						break;
					case FLOAT:
						*((Float*)insertAddr) = *((Float*)cvs.at(j).Value);
						insertAddr =  ((Float*)(insertAddr))+1;
						break;
					case INT:
						*((Int*)insertAddr) = *((Int*)cvs.at(j).Value);
						insertAddr =  ((Int*)(insertAddr))+1;
						break;
					case TIME:
						*((Time*)insertAddr) = *((Time*)cvs.at(j).Value);
						insertAddr =  ((Time*)(insertAddr))+1;
						break;
					case VARCHAR:
						*((String*)insertAddr) = *((String*)cvs.at(j).Value);
						insertAddr =  ((String*)(insertAddr))+1;
						break;
				}
				advance(i,1);
			}
		}
		if(oldAddr == insertAddr)
		{
			switch(i->GetType()) {
				case DATE:
					*((Date*)insertAddr) = Date(1,1,1);
					insertAddr =  ((Date*)(insertAddr))+1;
					break;
				case FLOAT:
					*((Float*)insertAddr) = Float(0);
					insertAddr =  ((Float*)(insertAddr))+1;
					break;
				case INT:
					*((Int*)insertAddr) = Int(0);
					insertAddr =  ((Int*)(insertAddr))+1;
					break;
				case TIME:
					*((Time*)insertAddr) = Time(1,1,1);
					insertAddr =  ((Time*)(insertAddr))+1;
					break;
				case VARCHAR:
					*((String*)insertAddr) = String("");
					insertAddr =  ((String*)(insertAddr))+1;
					break;
			}
		}
	}

	return newRecordAddr;
}

int SQLParser::ResolveSelect(vector<string> columns, vector<string> tables, Predicate* p, Entity *result)
{
	if(p == NULL)
	{
		result = &(*DB)[tables[0]];
		return 0;
	}
	else
	{
		result = (*DB)[tables[0]].Where(p);
		return 0;
	}
	return 1;
}
int SQLParser::ResolveInsert(string TableName, vector<ColumnValuePair> Values)
{
	Entity *e = &(*DB)[TableName];
	
	return e->InsertByAddr(createRowAddr(e->Columns, Values));
}
int SQLParser::ResolveUpdate(string TableName, vector<ColumnValuePair> Changes, Predicate* p)
{
	return 1;
}
int SQLParser::ResolveDelete(string TableName, Predicate* p)
{
	Entity *e = &(*DB)[TableName];

	return e->Remove(p);
}
int SQLParser::ResolveCreate(string TableName, vector<CD> columns)
{
	list<CD> a(columns.begin(), columns.end());
	return DB->Create(*(new Entity(TableName, a)));
}
int SQLParser::ResolveDrop(string TableName)
{
	Entity e = (*DB)[TableName];

	return DB->Delete(e);
}